#!/usr/bin/python3.6
import cgi
import cgitb
import hashlib
import sqlite3
from datetime import datetime as DateTime

from person import Person
from quote import Quote
from sanitize import sanitize_html
from templating import render

cgitb.enable()
print("Content-type: text/html\n")

form = cgi.FieldStorage()
try:
    num_quotes = int(form.getfirst("num-quotes", "100"))
except ValueError:
    num_quotes = 100
filter_author = form.getfirst("filter-author", "")
filter_text_context = form.getfirst("filter-text-context", "")
sorting = form.getfirst("sorting", "newest")
language = form.getfirst("language", "original")

connection = sqlite3.connect("quotes.sqlite")
cursor = connection.cursor()

banners = ""


def quote_exists(author: str, text: str) -> bool:
    """Checks if a quote from the given author with the given text exists in the database."""
    return bool(
        cursor.execute(
            "SELECT id FROM quotes WHERE author = ? AND text = ? LIMIT 1",
            [new_quote_author, new_quote_text],
        ).fetchall()
    )


def quote_valid(author: str, text: str) -> bool:
    return bool(author) and bool(text) and not quote_exists(author, text)


def get_fresh_id() -> str:
    """Gets the ID available for a new quote."""
    last_id_result = cursor.execute(
        "SELECT id FROM quotes WHERE id LIKE 'W%' ORDER BY LENGTH(id) DESC, id DESC LIMIT 1"
    ).fetchone()
    if last_id_result is not None:
        (last_id,) = last_id_result
        return f"W{int(last_id[1:]) + 1}"
    else:
        return "W0"


# Create new quote if asked to
new_quote_author = form.getfirst("new-quote-author")
new_quote_text = form.getfirst("new-quote-text")
if new_quote_author is not None and new_quote_text is not None:
    new_quote_context = form.getfirst("new-quote-context", "")
    new_quote_writer = form.getfirst("new-quote-writer", "")
    if quote_valid(new_quote_author, new_quote_text):
        quote = Quote(
            id=get_fresh_id(),
            author=Person.lookup_or_create(new_quote_author),
            text=new_quote_text,
            context=new_quote_context,
            datetime=DateTime.now(),
            writer=Person.lookup_or_create(new_quote_writer),
            text_en="",
            context_en="",
        )
        cursor.execute(
            f"INSERT INTO quotes VALUES ({','.join(':' + column for column in Quote.columns)})",
            quote.to_dict(anonymize=False),
        )
        connection.commit()


def can_edit_quote(key: str, quote: Quote) -> bool:
    """Determines if the given edit key can be used to edit the given quote."""
    global banners
    hashed_key = hashlib.sha256(key.encode("utf-8")).hexdigest()
    key_data = cursor.execute(
        "SELECT writer FROM keys WHERE hash = ?", [hashed_key]
    ).fetchone()
    if key_data is None:
        banners += '<div class="error">Tento klíč je neplatný.</div>'
        return False
    else:
        (allowed_writer_name,) = key_data
        if allowed_writer_name in [quote.writer.name, "*"]:
            return True
        else:
            banners += '<div class="error">K úpravě této hlášky nemáte oprávnění.</div>'
            return False


# Edit an existing quote if asked to and the edit key is correct
edited_quote_id = form.getfirst("edited-quote-id")
edited_quote_author = form.getfirst("edited-quote-author")
edited_quote_text = form.getfirst("edited-quote-text")
if (
    edited_quote_id is not None
    and edited_quote_author is not None
    and edited_quote_text is not None
):
    edit_key = form.getfirst("edit-key", "")
    edited_quote_context = form.getfirst("edited-quote-context", "")
    edited_quote_writer = form.getfirst("edited-quote-writer", "")
    edited_quote_text_en = form.getfirst("edited-quote-text-en", "")
    edited_quote_context_en = form.getfirst("edited-quote-context-en", "")
    quote = Quote.from_row(
        cursor.execute(
            f"SELECT {','.join(Quote.columns)} FROM quotes WHERE id = ? LIMIT 1",
            [edited_quote_id],
        ).fetchone()
    )
    if can_edit_quote(edit_key, quote):
        if edited_quote_text == "-":
            cursor.execute("DELETE FROM quotes WHERE id = ?", [edited_quote_id])
            connection.commit()
        else:
            edit_query = "UPDATE quotes SET text = ?, context = ?, writer = ?, text_en = ?, context_en = ?"
            edit_query_args = [
                edited_quote_text,
                edited_quote_context,
                edited_quote_writer,
                edited_quote_text_en,
                edited_quote_context_en,
            ]
            if edited_quote_author != "[anonymní]":
                edit_query += ", author = ?"
                edit_query_args.append(edited_quote_author)
            edit_query += " WHERE id = ?"
            edit_query_args.append(edited_quote_id)
            cursor.execute(edit_query, edit_query_args)
            connection.commit()

sorting_queries = {
    "newest": "datetime DESC, LENGTH(id) DESC, id DESC",
    "oldest": "datetime, LENGTH(id), id",
    "random": "RANDOM()",
}

query = f"SELECT {','.join(Quote.columns)} FROM quotes WHERE 0=0"
query_args = []
if filter_author:
    query += " AND INSTR(author, ?) <> 0"
    query_args.append(filter_author)
if filter_text_context:
    query += " AND (INSTR(text, ?) <> 0 OR INSTR(context, ?) <> 0)"
    query_args.append(filter_text_context)
    query_args.append(filter_text_context)
if language in ["english", "both"]:
    query += " AND text_en <> '' AND text_en <> '-'"
query += " ORDER BY " + sorting_queries[sorting]
if num_quotes:
    query += " LIMIT ?"
    query_args.append(num_quotes)
quotes = [Quote.from_row(row) for row in cursor.execute(query, query_args).fetchall()]
if filter_author:
    quotes = [quote for quote in quotes if not quote.author.anonymized]

names = set()
names |= {
    name for (name,) in cursor.execute("SELECT DISTINCT author FROM quotes").fetchall()
}
names |= {
    name for (name,) in cursor.execute("SELECT DISTINCT writer FROM quotes").fetchall()
}

num_total_quotes = cursor.execute("SELECT count(*) FROM quotes").fetchone()[0]

connection.close()

with open("templates/index.html", encoding="utf-8") as file:
    template = file.read()
print(
    render(
        template,
        {
            "banners": banners,
            "filter_author": sanitize_html(filter_author),
            "filter_text_context": sanitize_html(filter_text_context),
            "lang_original": "selected" * (language == "original"),
            "lang_english": "selected" * (language == "english"),
            "lang_both": "selected" * (language == "both"),
            "num_displayed_quotes": len(quotes),
            "num_quotes_100": "checked" * (num_quotes == 100),
            "num_quotes_all": "checked" * (num_quotes == 0),
            "num_total_quotes": num_total_quotes,
            "people_names_datalist": '<datalist id="people-names">'
            + "".join(
                f'<option value="{sanitize_html(name)}"></option>' for name in names
            )
            + "</datalist>",
            "quotes": "".join(quote.to_html(language) for quote in quotes),
            "sorting_newest": "checked" * (sorting == "newest"),
            "sorting_oldest": "checked" * (sorting == "oldest"),
            "sorting_random": "checked" * (sorting == "random"),
        },
    )
    .encode("ascii", "xmlcharrefreplace")
    .decode("ascii")
)
